<!DOCTYPE html>
<html>

<head>
  <!--
      参考https://www.jianshu.com/p/01e2d1fe1257
    -->
  <meta charset="UTF-8">
  <title>工程交互平台-零件数据监测</title>

  <link rel="stylesheet" href="./css/part.css">
  <!-- 引入图表库 -->
  <script src="./js/echarts.min.js"></script>
</head>

<body>
  <div class='container'>
    <table>
      <tr>
        <td class="buttons" colspan="4" style="white-space: nowrap; text-align:left;">
          <button onclick="render(this.innerHTML)">N350PPS</button>
          <button onclick="render(this.innerHTML)">N300PPS</button>
          <button onclick="render(this.innerHTML)">N300L</button>
          <button onclick="render(this.innerHTML)">N310</button>
        <td>
      </tr>
      <tr>
        <td colspan=5>
          <h1 style="margin-top:0"><text id='type'>{{type}}</text>车型前轮外倾关键零部件特性监测<h1>
        </td>
      </tr>
      <!-- 前轴-->
      <tr>
        <td class="with_border order">1</td>
        <td class="with_border part_name">前轴焊合件</td>
        <td class="with_border img"><img src="img/front_cross_member.png"></td>
        <td class="month_charts">
          <!-- 为 ECharts 准备一个定义了宽高的 DOM -->
          <div id="front_bolt_left_front" class="with_border month_chart"></div>
          <div id="front_bolt_left_back" class="with_border month_chart"></div>
          <div id="front_bolt_right_front" class="with_border month_chart"></div>
          <div id="front_bolt_right_back" class="with_border month_chart"></div>
        </td>
        <td class="day_charts">
          <div id="front_bolt_left_front_detail" class="with_border day_chart"></div>
          <div id="front_bolt_left_back_detail" class="with_border day_chart"></div>
          <div id="front_bolt_right_front_detail" class="with_border day_chart"></div>
          <div id="front_bolt_right_back_detail" class="with_border day_chart"></div>
        </td>
      </tr>
      <!-- 车身前悬安装点-->
      <tr>
        <td class="with_border order">2</td>
        <td class="with_border part_name">车身三坐标</td>
        <td class="with_border img"><img src="img/front_wheel_roof.png"></td>
        <td class="month_charts">
          <!-- 为 ECharts 准备一个定义了宽高的 DOM -->
          <div id="front_wheel_roof_left" class="with_border month_chart"></div>
          <div id="front_wheel_roof_right" class="with_border month_chart"></div>
        </td>
        <td class="day_charts">
          <div id="front_wheel_roof_left_detail" class="with_border day_chart"></div>
          <div id="front_wheel_roof_right_detail" class="with_border day_chart"></div>
        </td>
      </tr>
      <!-- 前弹簧-->
      <tr>
        <td class="with_border order">3</td>
        <td class="with_border part_name">前弹簧</td>
        <td class="with_border img"><img src="img/front_spring.png"></td>
        <td class="month_charts">
          <!-- 为 ECharts 准备一个定义了宽高的 DOM -->
          <div id="front_spring" class="with_border month_chart"></div>
        </td>
        <td class="day_charts">
          <div id="front_spring_detail" class="with_border day_chart"></div>
        </td>
      </tr>
      <!-- 前悬-->
      <tr>
        <td class="with_border order">4</td>
        <td class="with_border part_name">前悬</td>
        <td class="with_border img"><img src="img/suspension.png"></td>
        <td class="month_charts">
          <!-- 为 ECharts 准备一个定义了宽高的 DOM -->
          <div id="suspension" class="with_border month_chart"></div>
        </td>
        <td class="day_charts">
          <div id="suspension_detail" class="with_border day_chart"></div>
        </td>
      </tr>
      <!-- 前下摆臂-->
      <tr>
        <td class="with_border order">5</td>
        <td class="with_border part_name">前下摆臂</td>
        <td class="with_border img"><img src="img/cross_member.png"></td>
        <td class="month_charts">
          <!-- 为 ECharts 准备一个定义了宽高的 DOM -->
          <div id="cross_member" class="with_border month_chart"></div>
        </td>
        <td class="day_charts">
          <div id="cross_member_detail" class="with_border day_chart"></div>
        </td>
      </tr>
    </table>
    <script>
      //根据车型渲染
      function render(type) {
        document.querySelector('#type').innerHTML = type;
        let date = new Date();
        let params = { name: date.toISOString() };

        updateChart(params, frontBoltDetailRB, cmmMean, cmmErr, cmmSeries,true);
        updateChart(params, frontBoltDetailRF, cmmMean, cmmErr, cmmSeries,true);
        updateChart(params, frontBoltDetailLF, cmmMean, cmmErr, cmmSeries,true);
        updateChart(params, frontBoltDetailLB, cmmMean, cmmErr, cmmSeries,true);
        updateChart(params, frontWheelRoofDetailL, cmmMean, cmmErr, cmmSeries,true);
        updateChart(params, frontWheelRoofDetailR, cmmMean, cmmErr, cmmSeries,true);
        updateChart(params, frontSpringDetail, springMean, springErr, springSeries,true);
        updateChart(params, suspensionDetail, betaMean, betaErr, suspensionSeries,true);
        updateChart(params, crossMemberDetail, lengthMean, lengthErr, crossMemberSeries,true);

        updateChart(params, frontBoltRB, cmmMean, cmmErr, cmmSeries);
        updateChart(params, frontBoltRF, cmmMean, cmmErr, cmmSeries);
        updateChart(params, frontBoltLF, cmmMean, cmmErr, cmmSeries);
        updateChart(params, frontBoltLB, cmmMean, cmmErr, cmmSeries);
        updateChart(params, frontWheelRoofL, cmmMean, cmmErr, cmmSeries);
        updateChart(params, frontWheelRoofR, cmmMean, cmmErr, cmmSeries);
        updateChart(params, frontSpring, springMean, springErr, springSeries);
        updateChart(params, suspension, betaMean, betaErr, suspensionSeries);
        updateChart(params, crossMember, lengthMean, lengthErr, crossMemberSeries);



      }
      //前轴特征每月分布
      let frontBoltLF = echarts.init(document.getElementById('front_bolt_left_front'));
      let frontBoltLB = echarts.init(document.getElementById('front_bolt_left_back'));
      let frontBoltRF = echarts.init(document.getElementById('front_bolt_right_front'));
      let frontBoltRB = echarts.init(document.getElementById('front_bolt_right_back'));
      let frontBoltDetailLF = echarts.init(document.getElementById('front_bolt_left_front_detail'));
      let frontBoltDetailLB = echarts.init(document.getElementById('front_bolt_left_back_detail'));
      let frontBoltDetailRF = echarts.init(document.getElementById('front_bolt_right_front_detail'));
      let frontBoltDetailRB = echarts.init(document.getElementById('front_bolt_right_back_detail'));

      //轮罩
      let frontWheelRoofL = echarts.init(document.getElementById('front_wheel_roof_left'));
      let frontWheelRoofR = echarts.init(document.getElementById('front_wheel_roof_right'));
      let frontWheelRoofDetailL = echarts.init(document.getElementById('front_wheel_roof_left_detail'));
      let frontWheelRoofDetailR = echarts.init(document.getElementById('front_wheel_roof_right_detail'));

      //前弹簧
      let frontSpring = echarts.init(document.getElementById('front_spring'));
      let frontSpringDetail = echarts.init(document.getElementById('front_spring_detail'));


      //前悬
      let suspension = echarts.init(document.getElementById('suspension'));
      let suspensionDetail = echarts.init(document.getElementById('suspension_detail'));

      //前下摆臂
      let crossMember = echarts.init(document.getElementById('cross_member'));
      let crossMemberDetail = echarts.init(document.getElementById('cross_member_detail'));

      //三坐标标准
      let CMM_UP_LIMIT = 1.0; // 三坐标上限
      let CMM_UP_LIMIT_SEC = 0.75; //三坐标次上限
      let CMM_DOWN_LIMIT = -1.0; // 三坐标下限
      let CMM_DOWN_LIMIT_SEC = -0.75 //三坐标次下限

      //前弹簧空载力值标准
      let EMPTY_LOAD_UP = 3383 + 169;
      let EMPTY_LOAD_DOWN = 3383 - 169;

      //前悬beta角标准
      let BETA_UP = 5.36 + 0.3;
      let BETA_DOWN = 5.36 - 0.3;

      //前下摆臂长度标准
      let LENGTH_UP = 365.2 + 0.5;
      let LENGTH_DOWN = 365.2 - 0.5;

      //按正态分布获取二位小数的随机数
      function generateNormalDistribution(mean, stdd) {
        let u1 = Math.random();
        let u2 = Math.random();
        let z0 = mean + stdd * (Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2));
        return z0.toFixed(2);
      }

      //获取某一年所有的月份
      function getAllMonth(year) {
        let monthArray = [];
        for (let i = 1; i < 13; i++) {
          monthArray.push(i < 10 ? `${year}` + '-0' + `${i}` : `${year}-` + `${i}`);
        }
        return monthArray;
      }

      //获取某一月所有的日期
      function getAllDateInMonth(year, month) {
        var dateArray = [];
        var currentDate = new Date(year, month - 1, 1, 12);
        while (currentDate.getMonth() === month - 1) {
          dateArray.push(new Date(currentDate).toISOString().slice(0, 10));
          currentDate.setDate(currentDate.getDate() + 1);
        }
        return dateArray;
      }

      //获取单行测试数据
      function getRawTestData(dateArray, mean, err) {
        let data = [];
        let year = dateArray[0];
        let month = dateArray[1];
        let dates =
          dateArray.length == 1 ? getAllMonth(year) : getAllDateInMonth(year, month);
        for (date of dates) {
          data.push([date, generateNormalDistribution(mean, err)]);
        }
        return data;
      }

      /**
       * 获取测试数据
       * @param {array} dateArray 年份、月份构成的数组如[2023,1]
       * @param {number} mean 期望值
       * @param {number} err 均方差
       * @param {array} series 系列名称
       */
      function getTestData(dateArray, mean, err, series) {
        return series.map((item) => {
          let rawData = getRawTestData(dateArray, mean, err);
          return { name: item.name, data: rawData, isKeyValue: item.isKeyValue };
        });
      }

      /**
       * 从后端获取每月数据
       * @param {string} partName 零件名称
       * @param {string} character 零件特征
       * @param {Array} dateArray 包含年月的二维数组
       * @returns {Array} 
       * 
        [{name, //子系列名称 X、Y、Z向偏差
           data:[[]] //包含X、Y值的二维数组
           isKeyValue:boolean //标记是否关键特征系列
        }]
       */
      function getData(partName, character, dateArray, mean, err, series) {
        let data = [];
        //implement

        return data;
      }

      //填充markline标记线
      let CMM_MARKLINE = {
        symbol: 'none', label: { show: true }, lineStyle: { color: 'red' },
        data: [{ yAxis: CMM_UP_LIMIT }, { yAxis: CMM_DOWN_LIMIT },
        { yAxis: CMM_DOWN_LIMIT_SEC }, { yAxis: CMM_UP_LIMIT_SEC }]
      };
      let SPRING_MARKLINE = {
        symbol: 'none', label: { show: true }, lineStyle: { color: 'red' },
        data: [{ yAxis: EMPTY_LOAD_UP }, { yAxis: EMPTY_LOAD_DOWN }]
      }
      let BETA_MARKLINE = {
        symbol: 'none', label: { show: true }, lineStyle: { color: 'red' },
        data: [{ yAxis: BETA_UP }, { yAxis: BETA_DOWN }]
      }
      let CROSS_MEMBER_MARKLINE = {
        symbol: 'none', label: { show: true }, lineStyle: { color: 'red' },
        data: [{ yAxis: LENGTH_UP }, { yAxis: LENGTH_DOWN }]
      }

      /**
       * 获取图表的显示配置信息
       * @param {Array} mins 多重下限数组 仅支持长度为2
       * @param {Array} maxs 多重上限数组
       */
      function getVisualMap(mins, maxs) {
        let pieces = [];
        let visualMap = [];
        let color = ['green', 'cyan', 'blue'];
        mins.sort((a, b) => a - b);
        maxs.sort((a, b) => b - a);
        pieces.push({ max: mins[0], color: 'red' }, { min: maxs[0], color: 'red' });
        mins[1] && maxs[1] && pieces.push(
          { max: mins[0], min: mins[1], color: 'yellow' },
          { min: maxs[0], max: maxs[1], color: 'yellow' });
        visualMap.pieces = pieces;
        color.map((item, index) => {
          visualMap.push({
            type: 'piecewise',
            show: false,
            dimension: 1,
            seriesIndex: index,
            pieces: pieces,
            outOfRange: { color: color[index], },
          });
        });
        return visualMap;
      }

      /**
       * 获取一个简单的option，不包含数据
       * @param {string} title 图表的标题
       * @param {obejct} visualMap 常规及超限部分样式
       * @param {object} scale Y轴的范围
       * @returns {object} option
       */
      function getOption(title, visualMap, scale) {
        //定义整个option
        let option = {
          title: {
            text: title,
            left: 'center'
          },
          grid: {
            left: '5%',
            right: '5%',
            top: '10%',
            bottom: '20%'
          },
          tooltip: {
            trigger: 'axis',
            boundaryGap: false
          },
          xAxis: {
            type: 'category',
            data: null,
            axisLabel: {
              rotate: 30
            }
            //boundaryGap: false
          },
          yAxis: {
            type: 'value',
            splitLine: { show: false },
            min: scale.down,
            max: scale.top,
          },
          visualMap: visualMap,
          color: ['green', 'cyan', 'blue'],
          series: [],
          init: function (markLine, datas) {
            return initOption(this, markLine, datas)
          },
        };
        return option;

      }

      /**
       * 将数据填充至option
       * @param {object} option 要填充的option
       * @param {object} markLine 每个系列的上下限标记线
       * @param {array} datas 要填充的数据
       * @return {option} 填充好数据的option
       */
      function initOption(option, markLine, datas) {
        for (let data of datas)
          option.series.push({
            name: data.name,
            type: 'line',
            smooth: false,
            lineStyle: {
              width: data.isKeyValue ? 3 : 1 //是关键特征则加粗
            },
            markLine: markLine,
            label: { show: true, position: 'top', formatter: (params) => { return params.value[1] } },
            data: data.data,
          }
          );
        return option;
      }


      /**
       * 根据输入信息更新每日图表
       * @param {obejct} params 点击信息，提取年月
       * @param {object} chart 点击的图表对象
       * @param {number} mean 图线设计期望值
       * @param {number} err 图线设计偏差
       * @param {array} series 图线系列
       */
      function updateChart(params, chart, mean, err, series, isDetail) {
        let year = new Date(params.name).getFullYear();
        let month = new Date(params.name).getMonth() + 1;
        let option = chart.getOption();
        let markLine = option.series[0].markLine;
        let data = isDetail ? getTestData([year, month], mean, err, series) : getTestData([year], mean, err, series);
        //更新option
        option.series = [];
        option = initOption(option, markLine, data)
        //更新图表
        chart.setOption(option);
      }

      let year = new Date().getFullYear();
      let month = new Date().getMonth() + 1;

      //前轴
      let cmmMean = 0;
      let cmmErr = 0.53;
      let cmmScale = { top: 2, down: -2 };
      let cmmVisualMap = getVisualMap([CMM_DOWN_LIMIT, CMM_DOWN_LIMIT_SEC], [CMM_UP_LIMIT_SEC, CMM_UP_LIMIT]);
      let cmmSeries = [{ name: 'X向偏差', isKeyValue: false }, { name: 'Y向偏差', isKeyValue: true }, { name: 'Z向偏差', isKeyValue: false }];
      frontBoltOptionLF = getOption('左前孔每月偏差监测运行', cmmVisualMap, cmmScale).init(CMM_MARKLINE, getTestData([year], cmmMean, cmmErr, cmmSeries));
      frontBoltOptionLB = getOption('左后孔每月偏差监测运行', cmmVisualMap, cmmScale).init(CMM_MARKLINE, getTestData([year], cmmMean, cmmErr, cmmSeries));
      frontBoltOptionRF = getOption('右前孔每月偏差监测运行', cmmVisualMap, cmmScale).init(CMM_MARKLINE, getTestData([year], cmmMean, cmmErr, cmmSeries));
      frontBoltOptionRB = getOption('右后孔每月偏差监测运行', cmmVisualMap, cmmScale).init(CMM_MARKLINE, getTestData([year], cmmMean, cmmErr, cmmSeries));

      frontBoltOptionDetailLF = getOption('左前孔每日偏差监测运行', cmmVisualMap, cmmScale).init(CMM_MARKLINE, getTestData([year, month], cmmMean, cmmErr, cmmSeries));
      frontBoltOptionDetailLB = getOption('左后孔每日偏差监测运行', cmmVisualMap, cmmScale).init(CMM_MARKLINE, getTestData([year, month], cmmMean, cmmErr, cmmSeries));
      frontBoltOptionDetailRF = getOption('右前孔每日偏差监测运行', cmmVisualMap, cmmScale).init(CMM_MARKLINE, getTestData([year, month], cmmMean, cmmErr, cmmSeries));
      frontBoltOptionDetailRB = getOption('右后孔每日偏差监测运行', cmmVisualMap, cmmScale).init(CMM_MARKLINE, getTestData([year, month], cmmMean, cmmErr, cmmSeries));

      frontBoltLF.setOption(frontBoltOptionLF);
      frontBoltLB.setOption(frontBoltOptionLB);
      frontBoltRF.setOption(frontBoltOptionRF);
      frontBoltRB.setOption(frontBoltOptionRB);

      frontBoltDetailLF.setOption(frontBoltOptionDetailLF);
      frontBoltDetailLB.setOption(frontBoltOptionDetailLB);
      frontBoltDetailRF.setOption(frontBoltOptionDetailRF);
      frontBoltDetailRB.setOption(frontBoltOptionDetailRB);
      frontBoltLF.on('click', (params) => {
        updateChart(params, frontBoltDetailLF, cmmMean, cmmErr, cmmSeries, true);
      });
      frontBoltLB.on('click', (params) => {
        updateChart(params, frontBoltDetailLB, cmmMean, cmmErr, cmmSeries, true);
      });
      frontBoltRF.on('click', (params) => {
        updateChart(params, frontBoltDetailRF, cmmMean, cmmErr, cmmSeries, true);
      });
      frontBoltRB.on('click', (params) => {
        updateChart(params, frontBoltDetailRB, cmmMean, cmmErr, cmmSeries, true);
      });


      //前轮罩
      let frontWheelRoofOptionL = getOption('车身左前悬安装点每月监测', cmmVisualMap, cmmScale).init(CMM_MARKLINE, getTestData([year], cmmMean, cmmErr, cmmSeries));
      let frontWheelRoofOptionR = getOption('车身右前悬安装点每月监测', cmmVisualMap, cmmScale).init(CMM_MARKLINE, getTestData([year], cmmMean, cmmErr, cmmSeries));
      let frontWheelRoofDetailOptionL = getOption('车身左前悬安装点每日监测', cmmVisualMap, cmmScale).init(CMM_MARKLINE, getTestData([year, month], cmmMean, cmmErr, cmmSeries));
      let frontWheelRoofDetailOptionR = getOption('车身右前悬安装点每日监测', cmmVisualMap, cmmScale).init(CMM_MARKLINE, getTestData([year, month], cmmMean, cmmErr, cmmSeries));

      frontWheelRoofL.setOption(frontWheelRoofOptionL);
      frontWheelRoofR.setOption(frontWheelRoofOptionR);
      frontWheelRoofDetailL.setOption(frontWheelRoofDetailOptionL);
      frontWheelRoofDetailR.setOption(frontWheelRoofDetailOptionR);

      frontWheelRoofL.on('click', (params) => {
        updateChart(params, frontWheelRoofDetailL, cmmMean, cmmErr, cmmSeries, true);

      });
      frontWheelRoofR.on('click', (params) => {
        updateChart(params, frontWheelRoofDetailR, cmmMean, cmmErr, cmmSeries, true);
      });

      //前弹簧
      let springErr = 119;
      let springMean = 3383;
      let springScale = { down: 3000, top: 3650 };
      let springSeries = [{ name: '空载力值', isKeyValue: false }];
      let springVisualMap = getVisualMap([EMPTY_LOAD_DOWN], [EMPTY_LOAD_UP]);
      let frontSpringOption = getOption('前弹簧空载力值每月监测', springVisualMap, springScale).init(SPRING_MARKLINE, getTestData([year], springMean, springErr, springSeries));
      let frontSpringDetailOption = getOption('前弹簧空载力值每日监测', springVisualMap, springScale).init(SPRING_MARKLINE, getTestData([year, month], springMean, springErr, springSeries));
      frontSpring.setOption(frontSpringOption);
      frontSpringDetail.setOption(frontSpringDetailOption);
      frontSpring.on('click', (params) => {
        updateChart(params, frontSpringDetail, springMean, springErr, springSeries, true);
      });

      //前悬
      let betaErr = 0.21;
      let betaMean = 5.36;
      let betaScale = { down: 5.0, top: 6 };
      let suspensionSeries = [{ name: '前悬beta角值', isKeyValue: false }];
      let suspensionVisualMap = getVisualMap([BETA_DOWN], [BETA_UP]);
      let suspensionOption = getOption('前悬BETA角每月监测', suspensionVisualMap, betaScale).init(BETA_MARKLINE, getTestData([year], betaMean, betaErr, suspensionSeries, false));
      let suspensionDetailOption = getOption('前悬BETA角每日监测', suspensionVisualMap, betaScale).init(BETA_MARKLINE, getTestData([year, month], betaMean, betaErr, suspensionSeries));
      suspension.setOption(suspensionOption);
      suspensionDetail.setOption(suspensionDetailOption);
      suspension.on('click', (params) => {
        updateChart(params, suspensionDetail, betaMean, betaErr, suspensionSeries, true);
      });

      //前下摆臂长度
      let lengthErr = 0.35;
      let lengthMean = 365.2;
      let lengthScale = { down: 364, top: 366 };
      let crossMemberVisualMap = getVisualMap([LENGTH_DOWN], [LENGTH_UP]);
      let crossMemberSeries = [{ name: '前下摆臂长度', isKeyValue: false }];
      let crossMemberTestData = getTestData([year], lengthMean, lengthErr, crossMemberSeries, false);
      let crossMemberOption = getOption('前下摆臂长度每月监测', crossMemberVisualMap, lengthScale).init(CROSS_MEMBER_MARKLINE, crossMemberTestData);
      let crossMemberDetailTestData = getTestData([year, month], lengthMean, lengthErr, crossMemberSeries);
      let crossMemberDetailOption = getOption('前下摆臂长度每日监测', crossMemberVisualMap, lengthScale).init(CROSS_MEMBER_MARKLINE, crossMemberDetailTestData);
      crossMember.setOption(crossMemberOption);
      crossMemberDetail.setOption(crossMemberDetailOption);
      crossMember.on('click', (params) => {
        updateChart(params, crossMemberDetail, lengthMean, lengthErr, crossMemberSeries, true);
      });


      window.onload = render('N350PPS');
    </script>
  </div>
</body>

</html>